#!/usr/bin/env python
# -*- coding:UTF-8 -*-
# Project: devops
#  Author: an00
#  Create: 2018/6/24 17:03


from rest_framework import serializers
from servers.models import Server, NetworkDevice, IP
from manufacturer.models import Manufacturer, ProductModel


class ServerSerializer(serializers.ModelSerializer):

    last_check = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)
    ctime = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S', read_only=True)

    def get_ips(self, device_obj):
        result = []
        ips = device_obj.ip_set.all()
        for ip in ips:
            data = {
                'ip': ip.ip_addr,
                'netmask': ip.netmask
            }
            result.append(data)
        return result

    def get_network_device(self, server_obj):
        result = []
        devices = server_obj.networkdevice_set.all()
        for i in devices:
            data = {
                'name': i.name,
                'mac': i.mac_address,
                'ips': self.get_ips(i)
            }
            result.append(data)
        return result

    @staticmethod
    def get_idc_info(idc_obj):
        try:
            return {
                'id': idc_obj.id,
                'name': idc_obj.name
            }
        except:
            return {}

    @staticmethod
    def get_manufacturer_info(manufacturer_obj):
        try:
            return {
                'id': manufacturer_obj.id,
                'vendor_name': manufacturer_obj.vendor_name,
            }
        except:
            return {}

    @staticmethod
    def get_model_info(model_obj):
        try:
            return {
                'id': model_obj.id,
                'model_name': model_obj.model_name,
            }
        except:
            return {}

    @staticmethod
    def get_business_info(business_obj):
        try:
            return {
                'id': business_obj.id,
                'name': business_obj.name,
            }
        except:
            return {}

    @staticmethod
    def get_business_parent_info(business_parent_obj):
        try:
            return {
                'id': business_parent_obj.id,
                'name': business_parent_obj.name,
            }
        except:
            return {}

    @staticmethod
    def get_cabinet_info(cabinet_obj):
        try:
            return {
                'id': cabinet_obj.id,
                'name': cabinet_obj.name,
            }
        except:
            return {}

    def to_representation(self, instance):
        result = super().to_representation(instance)
        result['device'] = self.get_network_device(instance)
        result['idc'] = self.get_idc_info(instance.idc)
        result['manufacturer'] = self.get_manufacturer_info(instance.manufacturer)
        result['model_name'] = self.get_model_info(instance.model_name)
        result['cabinet'] = self.get_cabinet_info(instance.cabinet)
        result['business_line'] = self.get_business_info(instance.business_line)
        result['business_parent'] = self.get_business_parent_info(instance.business_parent)
        return result

    class Meta:
        """
        字段可能会增加，不能直接写全部，防止变更出错
        """
        model = Server
        fields = '__all__'


class ServerAutoReportSerializer(serializers.Serializer):
    ip = serializers.CharField(label='主机地址', help_text='主机地址', required=True)
    hostname = serializers.CharField(label='主机名', help_text='主机名', max_length=32, required=True)
    cpu = serializers.CharField(label='CPU', help_text='CPU', max_length=64, required=True)
    mem = serializers.CharField(label='内存', help_text='内存', max_length=32, required=True)
    disk = serializers.CharField(label='磁盘', help_text='磁盘', max_length=256, required=True)
    os = serializers.CharField(label='OS', help_text='操作系统', max_length=64, required=True)
    sn = serializers.CharField(label='SN', help_text='SN编码', max_length=64, required=True)
    manufacturer = serializers.CharField(label='制造厂商', help_text='制造厂商', max_length=32, required=True)
    model_name = serializers.CharField(label='服务器型号', help_text='服务器型号', max_length=64, required=True)
    uuid = serializers.CharField(label='UUID', help_text='虚拟机唯一标识', max_length=64, required=True)
    # network是不在模型中关联的，不能被传入创建server的参数中，包含设备的所有网卡和对应网络信息，是一个列表包含的字典
    network = serializers.JSONField(required=True)
    """
    network数据类似
    [{"name": "eth0",
      "ips": [{"ip_addr": "123.123.123.131", "netmask": "255.255.255.0"}],
      "mac_address": "a4:ba:db:21:4f:b4"},]
    """

    def validate_manufacturer(self, value):
        """
        字段验证
        manufacturer字段正常会被传入厂商名，若厂商不存在着创建再返回厂商对象，存在则直接返回该对象
        """
        try:
            return Manufacturer.objects.get(vendor_name__exact=value)
        except Manufacturer.DoesNotExist:
            return self.create_manufacturer(value)

    @staticmethod
    def create_manufacturer(value):
        """
        Manufacturer模型只有vendor_name字段必须，所以传入value创建
        """
        return Manufacturer.objects.create(vendor_name=value)

    def validate(self, attrs):
        """
        对象验证，验证失败应该触发异常raise serializers.ValidationError("message")
        :param attrs:
        :return:
        """
        manufacturer_obj = attrs['manufacturer']
        try:
            attrs['model_name'] = manufacturer_obj.productmodel_set.get(model_name__exact=attrs['model_name'])
        except ProductModel.DoesNotExist:
            attrs['model_name'] = self.create_productmodel(manufacturer_obj, attrs['model_name'])
        return attrs

    @staticmethod
    def create_productmodel(manufacturer_obj, model_name):
        """
        传入厂商对象与型号名称，厂商对象与设备型号关联，创建该型号
        """
        return ProductModel.objects.create(vendor=manufacturer_obj, model_name=model_name)

    def create_server(self, validated_data):
        """
        取出network并从校验的数据中删除，创建主机，检查是否有network中的网络设备
        check_network_device和它执行的一系列方法会创建网络设备和ip这些关联的字段
        """
        network = validated_data.pop('network')
        server_obj = Server.objects.create(**validated_data)
        self.check_network_device(server_obj, network)
        return server_obj

    def create(self, validated_data):
        """
        客户端只管post数据，这里用sn和uuid做判断是更新还是创建新server
        :param validated_data:
        :return: update / create server method
        """
        uuid = validated_data["uuid"].lower()
        sn = validated_data["sn"].lower()
        try:
            if sn == uuid or sn == '' or sn.startswith("vmware"):  # 虚拟机
                server_obj = Server.objects.get(uuid__icontains=uuid)
            else:  # 物理机
                server_obj = Server.objects.get(sn__icontains=sn)
        except Server.DoesNotExist:
            return self.create_server(validated_data)
        else:
            return self.update_server(server_obj, validated_data)

    def update_server(self, instance, validated_data):
        """
        服务器存在，更新服务器信息
        :param instance:
        :param validated_data:
        :return: server instance
        """
        instance.hostname = validated_data.get("hostname", instance.hostname)
        instance.cpu = validated_data.get("cpu", instance.cpu)
        instance.ip = validated_data.get("ip", instance.ip)
        instance.mem = validated_data.get("mem", instance.mem)
        instance.disk = validated_data.get("disk", instance.disk)
        instance.save()
        self.check_network_device(instance, validated_data["network"])
        return instance

    def check_network_device(self, server_obj, network):
        """
        存在则直检测是否有更新，不存在就创建网卡，关联server对象
        暂时没做下线处理，可以参考check_ip方法处理ip的下线情况
        """
        all_network_device = server_obj.networkdevice_set.all()
        current_dev = []  # 存放本次检测存在的网卡对象
        for device in network:
            ips = device.pop('ips')
            try:  # 获取设备关联的网卡名一致的记录，没有则创建网卡
                device_obj = all_network_device.get(name__exact=device["name"])
            except NetworkDevice.DoesNotExist:
                device_obj = self.create_network_device(server_obj, device)

            else:  # 若mac地址不一致，以最新检测到的为准，更新记录
                if device_obj.mac_address != device["mac_address"]:
                    device_obj.mac_address = device["mac_address"]
                    device_obj.save()
            self.check_ip(device_obj, ips)
            current_dev.append(device_obj)
        for dev_obj in set(all_network_device) - set(current_dev):  # 对比前后差异，删除下线网卡（本次检测不存在视为已下线）
            dev_obj.delete()

    @staticmethod
    def create_network_device(server_obj, device):
        device['host'] = server_obj
        device_obj = NetworkDevice.objects.create(**device)
        return device_obj

    @staticmethod
    def check_ip(device_obj, ips):
        """
        检测ip是否存在，若不在测创建。以当前上报为准，若已下线期则删除
        """
        all_interface = device_obj.ip_set.all()
        current_ip = []

        for interface in ips:
            try:  # 判断是否存在ip，存在会判断子网信息是否变动
                new_obj = all_interface.get(ip_addr__exact=interface['ip_addr'])
            except IP.DoesNotExist:
                new_obj = IP.objects.create(device=device_obj, netmask=interface['netmask'],
                                            ip_addr=interface['ip_addr'])
            else:
                if new_obj.netmask != interface['netmask']:  # 若地址子网有变动则修改
                    new_obj.netmask = interface['netmask']
                    new_obj.save()
            current_ip.append(new_obj)

        for ip_obj in set(all_interface) - set(current_ip):  # 删除过期的ip，以最新检测为准
            ip_obj.delete()

    def to_representation(self, instance):
        result = {
            "hostname": instance.hostname,
            "ip": instance.ip
        }
        return result


class NetworkDeviceSerializer(serializers.ModelSerializer):
    class Meta:
        model = NetworkDevice
        fields = '__all__'


class IPSerializer(serializers.ModelSerializer):
    class Meta:
        model = IP
        fields = '__all__'
